using JEngine.Core;
using LitJson;
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CL
{
    public enum CLEntityStatus : byte
    {
        None = 0,
        IsFromPool = 1,
        IsRegister = 1 << 1,
        IsComponent = 1 << 2,
        IsCreated = 1 << 3,
        IsNew = 1 << 4,
    }

    
    public abstract class CLEntity: DisposeObject
    {
        [JsonIgnore]
        private CLEntityStatus status = CLEntityStatus.None;
        [JsonIgnore]
        public long InstanceId { get; protected set; }
        
        //Entity唯一Id
        public long Id { get; protected set; }

        protected ICLScene iScene;
        [JsonIgnore]
        public ICLScene IScene
        {
            get
            {
                return this.iScene;
            }
            protected set
            {
                if (value == null)
                {
                    throw new Exception($"domain cant set null: {this.GetType().FullName}");
                }

                if (this.iScene == value)
                {
                    return;
                }

                ICLScene preScene = this.iScene;
                this.iScene = value;

                if (preScene == null)
                {
                    if (this.InstanceId == 0)
                    {
                        this.InstanceId = IdGenerater.Instance.GenerateInstanceId();
                    }

                    this.IsRegister = true;

                    // 反序列化出来的需要设置父子关系
                    if (this.componentsDB != null)
                    {
                        foreach (CLEntity component in this.componentsDB)
                        {
                            component.IsComponent = true;
                            this.Components.Add(this.GetLongHashCode(component.GetType()), component);
                            component.parent = this;
                        }
                    }

                    if (this.childrenDB != null)
                    {
                        foreach (CLEntity child in this.childrenDB)
                        {
                            child.IsComponent = false;
                            this.Children.Add(child.Id, child);
                            child.parent = this;
                        }
                    }
                }

                // 递归设置孩子的Domain
                if (this.children != null)
                {
                    foreach (CLEntity entity in this.children.Values)
                    {
                        entity.IScene = this.iScene;
                    }
                }

                if (this.components != null)
                {
                    foreach (CLEntity component in this.components.Values)
                    {
                        component.IScene = this.iScene;
                    }
                }

                if (!this.IsCreated)
                {
                    this.IsCreated = true;
                  //  CLEntitySystemSingleton.Instance.Deserialize(this);
                }
            }
        }
        [JsonIgnore]
        public bool IsFromPool
        {
            get => (this.status & CLEntityStatus.IsFromPool) == CLEntityStatus.IsFromPool;
            set
            {
                if (value)
                {
                    this.status |= CLEntityStatus.IsFromPool;
                }
                else
                {
                    this.status &= ~CLEntityStatus.IsFromPool;
                }
            }
        }
        
        [JsonIgnore]
        protected bool IsRegister
        {
            get => (this.status & CLEntityStatus.IsRegister) == CLEntityStatus.IsRegister;
            set
            {
                if (this.IsRegister == value)
                {
                    return;
                }

                if (value)
                {
                    this.status |= CLEntityStatus.IsRegister;
                }
                else
                {
                    this.status &= ~CLEntityStatus.IsRegister;
                }

                if (value)
                {
                    this.RegisterSystem();
                }
            }
        }

        protected virtual void RegisterSystem()
        {
            this.iScene.Fiber.EntitySystem.RegisterSystem(this);
        }

        protected virtual string ViewName
        {
            get
            {
                return this.GetType().FullName;
            }
        }

        [JsonIgnore]
        protected bool IsComponent
        {
            get => (this.status & CLEntityStatus.IsComponent) == CLEntityStatus.IsComponent;
            set
            {
                if (value)
                {
                    this.status |= CLEntityStatus.IsComponent;
                }
                else
                {
                    this.status &= ~CLEntityStatus.IsComponent;
                }
            }
        }
       
        [JsonIgnore]
        protected bool IsCreated
        {
            get => (this.status & CLEntityStatus.IsCreated) == CLEntityStatus.IsCreated;
            set
            {
                if (value)
                {
                    this.status |= CLEntityStatus.IsCreated;
                }
                else
                {
                    this.status &= ~CLEntityStatus.IsCreated;
                }
            }
        }

        [JsonIgnore]
        protected bool IsNew
        {
            get => (this.status & CLEntityStatus.IsNew) == CLEntityStatus.IsNew;
            set
            {
                if (value)
                {
                    this.status |= CLEntityStatus.IsNew;
                }
                else
                {
                    this.status &= ~CLEntityStatus.IsNew;
                }
            }
        }

        //判断是否销毁
        [JsonIgnore]
        public bool IsDisposed => this.InstanceId == 0;

        //父节点
        [JsonIgnore]
        private CLEntity parent;
        [JsonIgnore]
        public CLEntity Parent
        {
            get => this.parent;
            protected set
            {
                if (value == null)
                {
                    throw new Exception($"cant set parent null: {this.GetType().FullName}");
                }

                if (value == this)
                {
                    throw new Exception($"cant set parent self: {this.GetType().FullName}");
                }
                if (this.parent != null) // 之前有parent
                {
                    // parent相同，不设置
                    if (this.parent == value)
                    {
                        UnityEngine.Debug.LogError($"重复设置了Parent: {this.GetType().FullName} parent: {this.parent.GetType().FullName}");
                        return;
                    }

                    this.parent.RemoveFromChildren(this);
                }

                this.parent = value;
                this.IsComponent = false;
                this.parent.AddToChildren(this);

                if (this is ICLScene scene)
                {
                    scene.Fiber = this.parent.iScene.Fiber;
                    this.IScene = scene;
                }
                else
                {
                    this.IScene = this.parent.iScene;
                }
            }
        }
       
        [JsonIgnore]
        private CLEntity ComponentParent
        {
            set
            {
                if (value == null)
                {
                    throw new Exception($"cant set parent null: {this.GetType().FullName}");
                }

                if (value == this)
                {
                    throw new Exception($"cant set parent self: {this.GetType().FullName}");
                }

                // 严格限制parent必须要有domain,也就是说parent必须在数据树上面
                if (value.IScene == null)
                {
                    throw new Exception($"cant set parent because parent domain is null: {this.GetType().FullName} {value.GetType().FullName}");
                }

                if (this.parent != null) // 之前有parent
                {
                    // parent相同，不设置
                    if (this.parent == value)
                    {
                        UnityEngine.Debug.LogError($"重复设置了Parent: {this.GetType().FullName} parent: {this.parent.GetType().FullName}");
                        return;
                    }

                    this.parent.RemoveFromComponents(this);
                }

                this.parent = value;
                this.IsComponent = true;
                this.parent.AddToComponents(this);

                if (this is ICLScene scene)
                {
                    scene.Fiber = this.parent.iScene.Fiber;
                    this.IScene = scene;
                }
                else
                {
                    this.IScene = this.parent.iScene;
                }
            }
        }


        //获得父节点
        public T GetParent<T>() where T : CLEntity
        {
            return this.Parent as T;
        }

        //子节点存档
        protected List<CLEntity> childrenDB;

        //子节点
        [JsonIgnore]
        private SortedDictionary<long, CLEntity> children;
        
        [JsonIgnore]
        public SortedDictionary<long, CLEntity> Children
        {
            get
            {
                return this.children ??= ObjectPool.Instance.Fetch<SortedDictionary<long, CLEntity>>();
            }
        }

        //增加子节点
        private void AddToChildren(CLEntity entity)
        {
            this.Children.Add(entity.Id, entity);
        }

        private void RemoveFromChildren(CLEntity entity)
        {
            if (this.children == null)
            {
                return;
            }
            this.children.Remove(entity.Id);
            if (this.children.Count == 0)
            {
                ObjectPool.Instance.Recycle(this.children);
                this.children = null;
            }
        }

        //组件存档
        protected List<CLEntity> componentsDB;

        //组件
        [JsonIgnore]
        private SortedDictionary<long, CLEntity> components;
        [JsonIgnore]
        public SortedDictionary<long, CLEntity> Components
        {
            get
            {
                return this.components ??= ObjectPool.Instance.Fetch<SortedDictionary<long, CLEntity>>();
            }
        }


        private void AddToComponents(CLEntity component)
        {
            this.Components.Add(this.GetLongHashCode(component.GetType()), component);
        }

        private void RemoveFromComponents(CLEntity component)
        {
            if (this.components == null)
            {
                return;
            }

            this.components.Remove(this.GetLongHashCode(component.GetType()));

            if (this.components.Count == 0)
            {
                ObjectPool.Instance.Recycle(this.components);
                this.components = null;
            }
        }

        public int ComponentsCount()
        {
            if (this.components == null)
            {
                return 0;
            }
            return this.components.Count;
        }

        public int ChildrenCount()
        {
            if (this.children == null)
            {
                return 0;
            }
            return this.children.Count;
        }

        public K GetChild<K>(long id) where K : CLEntity
        {
            if (this.children == null)
            {
                return null;
            }

            this.children.TryGetValue(id, out CLEntity child);
            return child as K;
        }

        public void RemoveChild(long id)
        {
    
            if (this.children == null)
            {
                return;
            }
            if (!this.children.TryGetValue(id, out CLEntity child))
            {
                return;
            }
            this.children.Remove(id);
            child.Dispose();
        }

        public void RemoveComponent<K>() where K : CLEntity
        {
            if (this.IsDisposed)
            {
                return;
            }
            if (this.components == null)
            {
                return;
            }

            Type type = typeof(K);

            CLEntity c;
            if (!this.components.TryGetValue(this.GetLongHashCode(type), out c))
            {
                return;
            }

            this.RemoveFromComponents(c);
            c.Dispose();
        }

        private void RemoveComponent(CLEntity component)
        {
            if (this.IsDisposed)
            {
                return;
            }

            if (this.components == null)
            {
                return;
            }

            CLEntity c;
            if (!this.components.TryGetValue(this.GetLongHashCode(component.GetType()), out c))
            {
                return;
            }

            if (c.InstanceId != component.InstanceId)
            {
                return;
            }

            this.RemoveFromComponents(c);
            c.Dispose();
        }

        public void RemoveComponent(Type type)
        {
            if (this.IsDisposed)
            {
                return;
            }

            CLEntity c;
            if (!this.components.TryGetValue(this.GetLongHashCode(type), out c))
            {
                return;
            }

            RemoveFromComponents(c);
            c.Dispose();
        }

        public K GetComponent<K>() where K : CLEntity
        {
            if (this.components == null)
            {
                return null;
            }

            // 如果有IGetComponent接口，则触发GetComponentSystem
         /*   if (this is IGetComponentSys)
            {
                EntitySystemSingleton.Instance.GetComponentSys(this, typeof(K));
            }
*/
            CLEntity component;
            if (!this.components.TryGetValue(this.GetLongHashCode(typeof(K)), out component))
            {
                return default;
            }

            return (K)component;
        }

        public CLEntity GetComponent(Type type)
        {
            if (this.components == null)
            {
                return null;
            }

            // 如果有IGetComponent接口，则触发GetComponentSystem
            // 这个要在tryget之前调用，因为有可能components没有，但是执行GetComponentSystem后又有了
  /*          if (this is IGetComponentSys)
            {
                EntitySystemSingleton.Instance.GetComponentSys(this, type);
            }*/

            CLEntity component;
            if (!this.components.TryGetValue(this.GetLongHashCode(type), out component))
            {
                return null;
            }

            return component;
        }

        private static CLEntity Create(Type type, bool isFromPool)
        {
            CLEntity component;
            if (isFromPool)
            {
                component = (CLEntity)ObjectPool.Instance.Fetch(type);
            }
            else
            {
                component = Activator.CreateInstance(type) as CLEntity;
            }

            component.IsFromPool = isFromPool;
            component.IsCreated = true;
            component.IsNew = true;
            component.Id = 0;
            return component;
        }



        public CLEntity AddComponent(CLEntity component)
        {
            Type type = component.GetType();
            if (this.components != null && this.components.ContainsKey(this.GetLongHashCode(type)))
            {
                throw new Exception($"entity already has component: {type.FullName}");
            }

            component.ComponentParent = this;

            return component;
        }

        public CLEntity AddComponent(Type type, bool isFromPool = false)
        {
            if (this.components != null && this.components.ContainsKey(this.GetLongHashCode(type)))
            {
                throw new Exception($"entity already has component: {type.FullName}");
            }

            CLEntity component = Create(type, isFromPool);
            component.Id = this.Id;
            component.ComponentParent = this;
            CLEntitySystemSingleton entitySystemSingleton = CLEntitySystemSingleton.Instance;
            entitySystemSingleton.Awake(component);

            return component;
        }

        public K AddComponentWithId<K>(long id, bool isFromPool = false) where K : CLEntity, ICLAwake, new()
        {
            Type type = typeof(K);
            if (this.components != null && this.components.ContainsKey(this.GetLongHashCode(type)))
            {
                throw new Exception($"entity already has component: {type.FullName}");
            }

            CLEntity component = Create(type, isFromPool);
            component.Id = id;
            component.ComponentParent = this;
            CLEntitySystemSingleton entitySystemSingleton = CLEntitySystemSingleton.Instance;
            entitySystemSingleton.Awake(component);

            return component as K;
        }

        public K AddComponentWithId<K, P1>(long id, P1 p1, bool isFromPool = false) where K : CLEntity, ICLAwake<P1>, new()
        {
            Type type = typeof(K);
            if (this.components != null && this.components.ContainsKey(this.GetLongHashCode(type)))
            {
                throw new Exception($"entity already has component: {type.FullName}");
            }

            CLEntity component = Create(type, isFromPool);
            component.Id = id;
            component.ComponentParent = this;
            CLEntitySystemSingleton entitySystemSingleton = CLEntitySystemSingleton.Instance;
            entitySystemSingleton.Awake(component, p1);

            return component as K;
        }

        public K AddComponentWithId<K, P1, P2>(long id, P1 p1, P2 p2, bool isFromPool = false) where K : CLEntity, ICLAwake<P1, P2>, new()
        {
            Type type = typeof(K);
            if (this.components != null && this.components.ContainsKey(this.GetLongHashCode(type)))
            {
                throw new Exception($"entity already has component: {type.FullName}");
            }

            CLEntity component = Create(type, isFromPool);
            component.Id = id;
            component.ComponentParent = this;
            CLEntitySystemSingleton entitySystemSingleton = CLEntitySystemSingleton.Instance;
            entitySystemSingleton.Awake(component, p1, p2);

            return component as K;
        }

        public K AddComponentWithId<K, P1, P2, P3>(long id, P1 p1, P2 p2, P3 p3, bool isFromPool = false) where K : CLEntity, ICLAwake<P1, P2, P3>, new()
        {
            Type type = typeof(K);
            if (this.components != null && this.components.ContainsKey(this.GetLongHashCode(type)))
            {
                throw new Exception($"entity already has component: {type.FullName}");
            }

            CLEntity component = Create(type, isFromPool);
            component.Id = id;
            component.ComponentParent = this;
            CLEntitySystemSingleton entitySystemSingleton = CLEntitySystemSingleton.Instance;
            entitySystemSingleton.Awake(component, p1, p2, p3);

            return component as K;
        }

        public K AddComponent<K>(bool isFromPool = false) where K : CLEntity, ICLAwake, new()
        {
            return this.AddComponentWithId<K>(this.Id, isFromPool);
        }

        public K AddComponent<K, P1>(P1 p1, bool isFromPool = false) where K : CLEntity, ICLAwake<P1>, new()
        {
            return this.AddComponentWithId<K, P1>(this.Id, p1, isFromPool);
        }

        public K AddComponent<K, P1, P2>(P1 p1, P2 p2, bool isFromPool = false) where K : CLEntity, ICLAwake<P1, P2>, new()
        {
            return this.AddComponentWithId<K, P1, P2>(this.Id, p1, p2, isFromPool);
        }

        public K AddComponent<K, P1, P2, P3>(P1 p1, P2 p2, P3 p3, bool isFromPool = false) where K : CLEntity, ICLAwake<P1, P2, P3>, new()
        {
            return this.AddComponentWithId<K, P1, P2, P3>(this.Id, p1, p2, p3, isFromPool);
        }

        public CLEntity AddChild(CLEntity entity)
        {
            entity.Parent = this;
            return entity;
        }

        public T AddChild<T>(bool isFromPool = false) where T : CLEntity, ICLAwake
        {
            Type type = typeof(T);
            T component = (T)CLEntity.Create(type, isFromPool);
            component.Id = IdGenerater.Instance.GenerateId();
            component.Parent = this;

            CLEntitySystemSingleton.Instance.Awake(component);
            return component;
        }

        public T AddChild<T, A>(A a, bool isFromPool = false) where T : CLEntity, ICLAwake<A>
        {
            Type type = typeof(T);
            T component = (T)CLEntity.Create(type, isFromPool);
            component.Id = IdGenerater.Instance.GenerateId();
            component.Parent = this;

            CLEntitySystemSingleton.Instance.Awake(component, a);
            return component;
        }

        public T AddChild<T, A, B>(A a, B b, bool isFromPool = false) where T : CLEntity, ICLAwake<A, B>
        {
            Type type = typeof(T);
            T component = (T)CLEntity.Create(type, isFromPool);
            component.Id = IdGenerater.Instance.GenerateId();
            component.Parent = this;

            CLEntitySystemSingleton.Instance.Awake(component, a, b);
            return component;
        }

        public T AddChild<T, A, B, C>(A a, B b, C c, bool isFromPool = false) where T : CLEntity, ICLAwake<A, B, C>
        {
            Type type = typeof(T);
            T component = (T)CLEntity.Create(type, isFromPool);
            component.Id = IdGenerater.Instance.GenerateId();
            component.Parent = this;

            CLEntitySystemSingleton.Instance.Awake(component, a, b, c);
            return component;
        }

        public T AddChildWithId<T>(long id, bool isFromPool = false) where T : CLEntity, ICLAwake
        {
            Type type = typeof(T);
            T component = CLEntity.Create(type, isFromPool) as T;
            component.Id = id;
            component.Parent = this;
            CLEntitySystemSingleton.Instance.Awake(component);
            return component;
        }

        public T AddChildWithId<T, A>(long id, A a, bool isFromPool = false) where T : CLEntity, ICLAwake<A>
        {
            Type type = typeof(T);
            T component = (T)CLEntity.Create(type, isFromPool);
            component.Id = id;
            component.Parent = this;

            CLEntitySystemSingleton.Instance.Awake(component, a);
            return component;
        }

        public T AddChildWithId<T, A, B>(long id, A a, B b, bool isFromPool = false) where T : CLEntity, ICLAwake<A, B>
        {
            Type type = typeof(T);
            T component = (T)CLEntity.Create(type, isFromPool);
            component.Id = id;
            component.Parent = this;

            CLEntitySystemSingleton.Instance.Awake(component, a, b);
            return component;
        }

        public T AddChildWithId<T, A, B, C>(long id, A a, B b, C c, bool isFromPool = false) where T : CLEntity, ICLAwake<A, B, C>
        {
            Type type = typeof(T);
            T component = (T)CLEntity.Create(type, isFromPool);
            component.Id = id;
            component.Parent = this;

            CLEntitySystemSingleton.Instance.Awake(component, a, b, c);
            return component;
        }
        //释放
        public override void Dispose()
        {
            if (this.IsDisposed)
            {
                return;
            }

            this.IsRegister = false;
            this.InstanceId = 0;

            ObjectPool objectPool = ObjectPool.Instance;
            // 清理Children
            if (this.children != null)
            {
                foreach (CLEntity child in this.children.Values)
                {
                    child.Dispose();
                }

                this.children.Clear();
                objectPool.Recycle(this.children);
                this.children = null;

                if (this.childrenDB != null)
                {
                    this.childrenDB.Clear();
                    // 创建的才需要回到池中,从db中不需要回收
                    if (this.IsNew)
                    {
                        objectPool.Recycle(this.childrenDB);
                        this.childrenDB = null;
                    }
                }
            }

            // 清理Component
            if (this.components != null)
            {
                foreach (var kv in this.components)
                {
                    kv.Value.Dispose();
                }

                this.components.Clear();
                objectPool.Recycle(this.components);
                this.components = null;

                // 创建的才需要回到池中,从db中不需要回收
                if (this.componentsDB != null)
                {
                    this.componentsDB.Clear();
                    if (this.IsNew)
                    {
                        objectPool.Recycle(this.componentsDB);
                        this.componentsDB = null;
                    }
                }
            }

            // 触发Destroy事件
            if (this is ICLDestroy)
            {
                CLEntitySystemSingleton.Instance.Destroy(this);
            }

            this.iScene = null;

            if (this.parent != null && !this.parent.IsDisposed)
            {
                if (this.IsComponent)
                {
                    this.parent.RemoveComponent(this);
                }
                else
                {
                    this.parent.RemoveFromChildren(this);
                }
            }

            this.parent = null;

            base.Dispose();

            // 把status字段其它的status标记都还原
            bool isFromPool = this.IsFromPool;
            this.status = CLEntityStatus.None;
            this.IsFromPool = isFromPool;

            ObjectPool.Instance.Recycle(this);
        }

        protected long GetLongHashCode(Type type)
        {
           // return type.TypeHandle.Value.ToInt64();
            return type.FullName.GetHashCode();
        }

        public override void BeginInit()
        {
            //CLEntitySystemSingleton.Instance.Serialize(this);

            if (!this.IsCreated) return;

            this.componentsDB?.Clear();
            if (this.components != null && this.components.Count != 0)
            {
                ObjectPool objectPool = ObjectPool.Instance;
                foreach (CLEntity entity in this.components.Values)
                {
                    if (entity is not ICLSerializeToEntity)
                    {
                        continue;
                    }

                    this.componentsDB ??= objectPool.Fetch<List<CLEntity>>();
                    this.componentsDB.Add(entity);

                    entity.BeginInit();
                }
            }

            this.childrenDB?.Clear();
            if (this.children != null && this.children.Count != 0)
            {
                ObjectPool objectPool = ObjectPool.Instance;
                foreach (CLEntity entity in this.children.Values)
                {
                    if (entity is not ICLSerializeToEntity)
                    {
                        continue;
                    }

                    this.childrenDB ??= objectPool.Fetch<List<CLEntity>>();
                    this.childrenDB.Add(entity);

                    entity.BeginInit();
                }
            }
        }
    }
}
